home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Mac100% 1998 November
/
MAC100-1998-11.ISO.7z
/
MAC100-1998-11.ISO
/
オンラインソフト定点観測
/
ユーティリティ
/
Mops 3.2.sea
/
Mops 3.2
/
Mops source
/
PPC source
/
qpCond
< prev
next >
Wrap
Text File
|
1998-06-06
|
18KB
|
796 lines
(* ============================
Here we redefine various compiling and immediate words.
Note we load this file twice - we need the new definitions in 68k mode so we can
target compile the code generator, then after it's loaded we need the new
definitions in PPC mode. We manage this with appropriate conditional compilation.
It's far better to do this with a single file rather than two files which could
easily get out of sync with each other.
============================
*)
forward toObjPtr
: (") { ¥ addr len -- }
& " parse -> len -> addr
will_skip? ?EXIT ¥ no need to store the string if the
¥ code won't be executed!
len +C: const_data ¥ put length byte in front for Pascal
const_data_ref ¥ compile push of addr
len postpone literal ¥ compile push of len
addr len add: const_data ¥ put string in const_data area
0 +C: const_data ¥ add a zero at the end for C
;
PPC? not
[IF] ¥ in ppc mode, these are in zBase.
: " { ¥ addr len -- }
PPC?
IF state
IF (") ¥ compiling
ELSE & " parse ¥ interpreting
THEN
ELSE
postpone "
THEN
; immediate
: "S postpone " ; immediate ¥ ANSI synonym for "
: ." { ¥ addr len -- }
PPC?
IF state
IF (") ¥ compiling
" type" evaluate ¥ late bind so we get PPC 'type'
ELSE & " parse type ¥ interpreting
THEN
ELSE
postpone ."
THEN
; immediate
: ABORT"
postpone "
" do_abq" evaluate ¥ we need the PPC versions of this!
; immediate
(* -> might be a bit more logical in qCreate, but that comes after
qCase which redefines SELECT[ etc. which we need here with its
old meaning.
-> is similar to what was defined in Base, but if we're in the PPC
image we have to take special action for Values and Vects, since
they're stored differently on the PPC.
*)
: ->
crossed? NIF postpone -> EXIT THEN
prfToken
SELECT[ objPtrTyp ]=> toObjPtr EXIT
[ valTyp ]=> state NIF 2+ @abs ! EXIT THEN
[ vecTyp ]=> state NIF 2+ reloc! EXIT THEN
DEFAULT=> drop
]SELECT
$ 60 ( opcode for store ) ,exec
; immediate
[THEN]
¥ ====================== DO loops etc. ======================
(*
Equalization of DO loops has a couple of entertaining features which add
to life's rich tapestry:
1. I can be returned on the stack when the loop ends. Since
I gets restored by the windup code, we have to do an equalization
first so that any reference to I on the stack gets converted to
a normal register.
2. LEAVE has to be handled properly - so before compiling the
branch, we have to equalize to whatever we'll have at the end
of the loop.
These considerations mean that we have to have something standard to
equalize to at the end of the loop, and we might need to know this
before we get there. So we just pick something a bit arbitrary
but sensible - using our return_cnt mechanism is probably as good
as anything.
*)
¥ I_reg gpr I ¥ ###### MUST use while testing without loading
¥ setup etc.
(* ?adjust_I_and_branch is called to handle the end of a DO or FOR loop, in the cases
where I gets a fixed increment.
If the ctr wasn't clobbered in the loop, we generate a bdnz for the branch, which
is faster than a test on I and then branch on condition.
Note though, that if the ctr was clobbered in the loop, this means there was an
inner loop or an external call, and in these situations the extra overhead is
insignificant.
*)
: ?adjust_I_and_branch { increment for? -- }
¥ we only bother decrementing I if it's been used in the loop.
I_reg select: GPRs
get: ivar> lastRefCDP in GPRs loop_start u>=
ctr_clobbered? or
IF increment postpone literal " ++> i" evaluate THEN
ctr_clobbered?
IF for?
IF " i 0>=" evaluate
ELSE
" i do_limit" evaluate
increment 0>
IF postpone <
ELSE postpone >=
THEN
THEN
false pif
ELSE
$ 42000000 code, ¥ bdnz
THEN
<resolve
true -> ctr_clobbered? ¥ if it wasn't before, it is now!
;
ppc? [if] hexx [else] hex [then]
: windup_loop { flags for? restore_do_limit? RP_increment -- }
BF050000 code, ¥ LEAVEs resolve to here. This pseudo-instrn
¥ allows resolution at finalization time. Gets
¥ replaced by the first instruction of the windup
¥ sequence, namely:
¥ lwz r21/I, (r17/RP) restore I
¥ It doesn't matter that we may be doing another
¥ equalization before the rest of the windup
¥ sequence, since equalization doesn't affect
¥ r22 or the return stack.
¥ now the rest of the windup sequence:
restore_do_limit? ¥
IF 82D10004 code, ¥ lwz r22/do_limit, 4(r17/RP) and do limit reg if nec
THEN
3A310000 RP_increment or
code, ¥ addi r17/RP, r17/RP, incr - adjust RP
¥ Note: RP_increment is now always 8. We should get rid of this as a parameter.
flags 8 and
IF " else 2drop then" evaluate
THEN
for?
IF " else drop then" evaluate
THEN
;
¥ I'll define UNLOOP and UNFOR here, since they're very similar to the above, but
¥ simpler. We assume I is getting used and restore it (it may not have been
¥ used in the loop yet, but may before the end).
: UNLOOP
get_loop_cnts simple_equalize ¥ since a ref to I might be on the stack,
¥ and we're about to clobber it!
82D10004 code, ¥ lwz r22/do_limit, 4(r17/RP) and do limit reg
82B10000 code, ¥ lwz r21/I, (r17/RP) and I
3A310008 code, ¥ addi r17/RP, r17/RP, 8 - adjust RP
;
ppc? [if] ppc_immediate [else] immediate [then]
: UNFOR
get_loop_cnts simple_equalize ¥ since a ref to I might be on the stack,
¥ and we're about to clobber it!
82B10000 code, ¥ lwz r21/I, (r17/RP) and I
3A310008 code, ¥ addi r17/RP, r17/RP, 8 - adjust RP
;
ppc? [if] ppc_immediate [else] immediate [then]
ppc? [if] decimalx [else] decimal [then]
: (WHILE) { x1 Nwhile? ¥ svCS svCF -- x1 }
pop: control_stk -> svCS pop: control_flags -> svCF
restore: fcstk_temp restore: cstk_temp
Nwhile? IF " nif" ELSE " if" THEN evaluate
save: cstk_temp save: fcstk_temp
svCF push: control_flags svCS push: control_stk
x1 ;
ppc?
[IF]
: IF true pif >mark ; ppc_immediate
: NIF false pif >mark ; ppc_immediate
: ELSE (else) ; ppc_immediate
: THEN >resolve&equalize ; ppc_immediate
: BEGIN <mark ; ppc_immediate
: WHILE false (while) ; ppc_immediate
: NWHILE true (while) ; ppc_immediate
: UNTIL true pif <resolve ; ppc_immediate
: NUNTIL false pif <resolve ; ppc_immediate
: AGAIN $ BF080000 code, <resolve ; ppc_immediate
: REPEAT ( postpone again postpone then ) ¥ AGAIN and THEN *must* be ppc_immediate,
¥ so we can't see them to call them!
$ BF080000 code, <resolve >resolve&equalize ; ppc_immediate
[ELSE]
: IF ppc?
IF true pif >mark
ELSE postpone if
THEN
; immediate
: NIF ppc?
IF false pif >mark
ELSE postpone nif
THEN
; immediate
: ELSE ppc?
IF (else)
ELSE postpone else
THEN
; immediate
: THEN ppc?
IF >resolve&equalize
ELSE postpone then
THEN
; immediate
: BEGIN ppc?
IF <mark
ELSE postpone begin
THEN
; immediate
: WHILE ppc?
IF false (while)
ELSE postpone while
THEN
; immediate
: NWHILE ppc?
IF true (while)
ELSE postpone nwhile
THEN
; immediate
: UNTIL ppc?
IF true pif <resolve
ELSE postpone until
THEN
; immediate
: NUNTIL ppc?
IF false pif <resolve
ELSE postpone nuntil
THEN
; immediate
: AGAIN ppc?
IF $ BF080000 code,
<resolve
ELSE
postpone again
THEN
; immediate
: REPEAT ppc?
IF postpone again
postpone then
ELSE
postpone repeat
THEN
; immediate
[THEN]
¥ For FOR loops, we initially copy i to the ctr then decrement i. At NEXT,
¥ we decrement i and branch back on the ctr being nonzero, unless it was
¥ clobbered in the loop.
: FOR
" dup 0> if" evaluate ¥ we always include a neg/zero bailout test here
" i >r -> i" evaluate
i_reg gpr>ctr
" -1 ++> i" evaluate
<mark
false -> ctr_clobbered?
;
ppc? [if] ppc_immediate [else] immediate [then]
: NEXT
-1 true ?adjust_I_and_branch
false true false 8 windup_loop
;
ppc? [if] ppc_immediate [else] immediate [then]
ppc?
[IF]
: DO ¥ ( limit init-index -- )
" do_limit >rw i >rw -> i -> do_limit do_limit i -" evaluate
0 push: control_flags ¥ tell LOOP there's no test to resolve
0 push: control_stk ¥ dummy
1 operands gpr: opnd1 gpr>ctr
free: opnd1
<mark
false -> ctr_clobbered? ; ppc_immediate
: ?DO
" 2dup - -> treg treg 0> if" evaluate
" do_limit >rw i >rw -> i -> do_limit" evaluate
8 push: control_flags ¥ tell LOOP there's a test to resolve
0 push: control_stk ¥ dummy
¥ 1 operands gpr: opnd1 gpr>ctr
0 gpr>ctr
free: opnd1
<mark
false -> ctr_clobbered? ; ppc_immediate
: LOOP
1 false ?adjust_I_and_branch
pop: control_stk drop
pop: control_flags false true 8 windup_loop ; ppc_immediate
: +LOOP
" dup ++> i i do_limit rot 0>= if < else >= then" evaluate
false pif
<resolve
pop: control_stk drop
pop: control_flags false true 8 windup_loop ; ppc_immediate
[ELSE]
: DO ¥ ( limit init-index -- )
PPC?
IF " do_limit >rw i >rw -> i -> do_limit do_limit i -" evaluate
0 push: control_flags ¥ tell LOOP there's no test to resolve
0 push: control_stk ¥ dummy
1 operands gpr: opnd1 gpr>ctr
free: opnd1
<mark
false -> ctr_clobbered?
ELSE
postpone do
THEN ; immediate
: ?DO
PPC?
IF " 2dup - -> treg treg 0> if" evaluate
" do_limit >rw i >rw -> i -> do_limit" evaluate
8 push: control_flags ¥ tell LOOP there's a test to resolve
0 push: control_stk ¥ dummy
0 gpr>ctr
free: opnd1
<mark
false -> ctr_clobbered?
ELSE
postpone ?do
THEN ; immediate
: LOOP
PPC?
IF 1 false ?adjust_I_and_branch
pop: control_stk drop
pop: control_flags false true 8 windup_loop
ELSE
postpone loop
THEN
; immediate
: +LOOP
PPC?
IF " dup ++> i i do_limit rot 0>= if < else >= then" evaluate
false pif
<resolve
pop: control_stk drop
pop: control_flags false true 8 windup_loop
ELSE
postpone +loop
THEN
; immediate
[THEN]
: LEAVE ¥ we use a pseudo-op, and resolve during finalization.
get_loop_cnts simple_equalize ¥ first we must equalize to the
¥ loop end since we're branching
¥ there.
$ BF040000 code,
;
ppc? [if] ppc_immediate [else] immediate [then]
¥ these redefinitions have to be left to the end since the originals
¥ get used...
(* Tick on the PPC will return the addr of the byte following the handler
field, as on the 68k. This isn't 4-byte aligned on the PPC, but in
lots of places we do ['] something 2- w@ to get the handler code,
so we'll stick with it.
We need to be very careful about ticking something that isn't code.
ANSI rightly says that this is undefined, but I think I've done it
in various places in the Mops code, e.g. to get a data address. This
will be invalid on the PPC, and may cause nasty bugs, so I want to
be sure I catch these situations.
On the PPC we can always distinguish code from non-code definitions
- the latter have handler codes BCxx. So I now make ['] do a check,
and give an error on words with BCxx, except for the few exceptions
such as vectors, for which ticking is legal.
I've provided <'> as a version of ['] that omits the check, so that
I can do it if I know it's OK.
Seeing we're looking at the handler code anyway when we tick, we
might as well simplfy :proc words by automatically picking up the
UPP address if we're running on the PPC.
*)
ppc?
[IF]
: ' ( -- xt )
defined? ?notfound ;
: ['] { ¥ hdlr -- xt }
'
dup 2- w@ -> hdlr
¥ Normally we won't allow ticking of BC words, but we need to
¥ allow a few of them for which EXECUTEing an xt makes sense.
¥ This means that EXECUTE (in Setup) must special-case each of
¥ these.
hdlr 8 >> $ BC =
IF
hdlr
CASE[ $ BC1D ]=> ¥ class_h
¥ [ $ BC02 ]=> ¥ const_h
[ $ BC05 ]=> ¥ vect_h
[ $ BC0C ]=> ¥ does_h
[ $ BC41 ]=> ¥ marker_h
DEFAULT=> 215 die ¥ "can't tick that kind of word"
]CASE
THEN
lit_addr
hdlr $ BE04 =
IF ¥ it's a :proc word - replace xt with UPP
" 2+ @abs @" evaluate
THEN
; ppc_immediate
: <'> ' lit_addr ; ppc_immediate
(*
-> (immediate, compilation only) compiles a store to a value
or a vect by passing the otStore opcode to its compilation handler.
This is an interim scheme until -> is redefined in zArgs.
*)
: ->
?comp
' otStore (compN) ; ppc_immediate
: ++>
?comp
' otAdd (compN) ; ppc_immediate
(* For forward definitions, we can't tell how many parms we'll need in
regs, or how many results there are. So we just assume there are no
named parms/results (which will lead to call_cnt cells being in regs),
and 1 result. We then work to this specification when the forward
definition is resolved.
*)
: unresolved
r@ 6 - .id
109 die
;
: FORWARD
ppc_header
$ BE010100 code, ¥ $ BE01 = forward defn
¥ $ 0100 = 0 flags, 1 = 1 stk result,
¥ 0 = no named parms, 0 = zero total parms/locals
['] unresolved 2+ CDP - $ 03FFFFFF and $ 48000001 or code,
; ppc_only
: :F
' 2+ -> ^fwd
CDP -> const_data_start
$ BF060000 code,
true ppc_entry
fwd_gpr_rtn_cnt -> gpr_rtn_cnt
fwd_fpr_rtn_cnt -> fpr_rtn_cnt
drop 301
; ppc_only
: ;F
301 ?defn
curr-def 2- (;) ¥ similar to "postpone ;" which we can't do here
^fwd curr-def resolve_unconditional_branch
; ppc_immediate
[ELSE]
: ['] ppc?
IF '
¥ should I allow ticking of BC words? I first thought not, but it's
¥ useful to be able to tick a couple of them which are safe.
dup 2- c@ $ BC =
IF
dup 2- w@
CASE[ $ BC1D ]=> ¥ class_h
[ $ BC02 ]=> ¥ const_h
[ $ BC05 ]=> ¥ vect_h
[ $ BC0C ]=> ¥ does_h
DEFAULT=> 215 die ¥ "can't tick that kind of word"
]CASE
THEN
lit_addr
ELSE
postpone [']
THEN
; immediate
: <'>
ppc?
IF ' lit_addr
ELSE postpone [']
THEN
; immediate
(* For forward definitions, we can't tell how many parms we'll need in
regs, or how many results there are. So we just assume there are no
named parms/results (which will lead to call_cnt cells being in regs),
and 1 result. We then work to this specification when the forward
definition is resolved.
*)
: FORWARD
ppc?
IF ppc_header
$ BE010100 code, ¥ $ BE01 = forward defn
¥ $ 0100 = 0 flags, 1 = 1 stk result,
¥ 0 = no named parms, 0 = zero total parms/locals
$ 48000000 code,
ELSE
forward
THEN
;
: :F
ppc?
IF ' 2+ -> ^fwd
CDP -> const_data_start
$ BF060000 code,
true ppc_entry
fwd_gpr_rtn_cnt -> gpr_rtn_cnt
fwd_fpr_rtn_cnt -> fpr_rtn_cnt
drop 301
ELSE
:f
THEN
;
: ;F
ppc?
IF 301 ?defn 300 postpone ;
^fwd curr-def resolve_unconditional_branch
ELSE
postpone ;f
THEN
; immediate
: +ECHOx +echo ; ¥ so I can redefine +ECHO and still be able to call the
¥ 68k one
: -ECHOx -echo ;
[THEN]
ppc?
[IF]
: >R true (>r) ; ppc_immediate
: R> true (r>) ; ppc_immediate
: >RW false (>r) ; ppc_immediate
: R>W false (r>) ; ppc_immediate
: R@ ( -- n ) ¥ We handle this as a normal fetch using RP as the
¥ base, with a zero displacement. But note that
¥ we mustn't hoist this fetch past any change of
¥ RP. Since we don't record changes to RP, we just
¥ don't hoist at all. R@ is so rare that it's not
¥ worth doing anything beyond this simple approach.
hoist? false -> hoist? ¥ mustn'
postpone RP postpone @
( false -> leaf? )
-> hoist?
; ppc_immediate
(* The idea of the "false -> leaf?" was, that if we're in a leaf
proc, the return addr isn't on the return stack, and this might
break some code that tries to access the rtn addr with rtn stack
operations. But this sort of monkeying with the rtn addr is highly
nonstandard, and would never work anyway if there are locals, so
we're not going to support it.
*)
[ELSE]
: >R ppc?
IF true (>r)
ELSE
postpone >r
THEN
; immediate
: R> ppc?
IF true (r>)
ELSE
postpone r>
THEN
; immediate
¥ These 2 are only used when PPC? is true.
: >RW false (>r) ; immediate
: R>W false (r>) ; immediate
: R@ ppc?
IF
hoist? false -> hoist?
postpone RP postpone @
( false -> leaf? )
-> hoist?
ELSE
postpone r@
THEN
; immediate
[THEN]
0 gpr TREG ¥ can't redefine this till after all 68k-style
¥ SELECT[ and CASE[ !!
¥ In this system, compilation is done by executing the compilation handler
¥ for the word in question. POSTPONE must therefore be immediate, and
¥ compile the right code into the client definition. This code consists
¥ of a literal push of the POSTPONEd word's cfa, then a call to (COMP).
ppc?
[IF]
: POSTPONE
defined?
dup 0<
IF ¥ not immediate - compile code to compile it
drop
lit_addr
postpone (comp)
ELSE
0> IF ¥ immediate - compile it now
(comp)
ELSE
0 ?notFound ¥ force a "not found" error
THEN
THEN
; ppc_immediate
marker m__zcase
endload
[ELSE]
: POSTPONE
ppc?
IF defined?
dup 0<
IF ¥ not immediate - compile code to compile it
drop lit_addr " (comp)" evaluate
¥ PPC (comp) not defined yet!
ELSE
0> IF ¥ immediate - compile it now
(comp)
ELSE
0 ?notFound ¥ force a "not found" error
THEN
THEN
ELSE
postpone postpone
THEN
; immediate
(* When target compiling, we use the $20 bit in the length byte of the
header, to tell the 68k FIND that we don't want to find these words
on the 68k. But they'll be found by the PPC FIND, since it won't look
at this bit. This is vital for handling words like IF in a sensible
manner.
We invoke this feature by using ppc_only or ppc_immediate, similarly
to immediate (i.e. straight after the definition it applies to).
We make these words immediate so we can use them within the defn
itself - e.g. in the defn for ; we have to specify it as ppc_immediate
before we hit its final ; for obvious reasons!
*)
: PPC_ONLY ¥ sets PPC-only bit
$ 20 latest cset ; immediate
: PPC_IMMEDIATE ¥ sets immediate and PPC-only bits
$ 60 latest cset ; immediate
[THEN]